package com.yanqu.road.server.connect;

import com.yanqu.road.connect.ServerConnectionGroup;
import com.yanqu.road.NettyMgr;
import com.yanqu.road.connect.Connection;
import com.yanqu.road.connect.YanQuConnection;
import com.yanqu.road.pb.server.LoadProto;
import com.yanqu.road.utils.YanQuMessageUtils;
import com.yanqu.road.message.YanQuMessage;
import com.yanqu.road.server.protocol.AgentProtocol;
import com.yanqu.road.utils.ConfigHelper;
import io.netty.channel.ChannelHandlerContext;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

public class CrossAgentLinkedControl {
	private static Logger logger = LogManager.getLogger(CrossAgentLinkedControl.class.getName());

	private static Map<Long, Integer> agentCountMap = new ConcurrentHashMap<>();
	private static Map<Long, Map<Integer, ServerConnectionGroup>> agentServerLinkedSet = new ConcurrentHashMap<>();
	private static Object lockObj = new Object();
	private static int checkIndex = 0;

	private CrossAgentLinkedControl() {

	}

	public static void init(long serverId, int count) {
		agentCountMap.put(serverId, count);
	}

	public static void removeLinkedClient(ChannelHandlerContext ctx) {
		ServerConnectionGroup client = getLinkedClient(ctx);
		if (client == null) {
			return;
		}

		if (!agentServerLinkedSet.containsKey(client.getServerId())) {
			return;
		}
		int id = Long.valueOf(client.getConnectionId()).intValue();
		logger.error("removeLinkedClient --- removeId : {}", id);
		if (agentServerLinkedSet.get(client.getServerId()).containsKey(id)) {
			agentServerLinkedSet.get(client.getServerId()).remove(id);
		}

	}

	public static ServerConnectionGroup getLinkedClient(ChannelHandlerContext ctx) {
		ServerConnectionGroup client = ctx.channel().attr(NettyMgr.LINKED_CLIENT).get();
		return client;
	}

	public static void send(long serverId, long userId, YanQuMessage packet) {
		if (!agentServerLinkedSet.containsKey(serverId) || !agentCountMap.containsKey(serverId)) {
			logger.info("code:{}, Can not found agent connection , serverId:{} ,userId:{}", packet.getCode(), serverId, userId);
			return;
		}


//		if (userId <= 0) {
//			sendSingle(serverId, packet);
//		} else {

		if (userId < 0) {
			userId = 0;
		}

			int idx = (int) (userId % agentCountMap.get(serverId));
			ServerConnectionGroup group = agentServerLinkedSet.get(serverId).get(idx);
			if (group != null) {
				group.sendPacket(userId, packet);
			} else {
				logger.error("Can not found agent connection , serverId = " + serverId + " , userId = " + userId
						+ " code : " + packet.getCode() + " . packet forward failed." + " .idx : " + idx
						+ "  .agentLinkedSet size:" + agentServerLinkedSet.get(serverId).size());
			}
//		}
	}

	public static void sendToAll(YanQuMessage packet) {
		if (agentCountMap != null) {
			for (long serverId : agentCountMap.keySet()) {
				send(serverId, 1, packet);
			}
		}
	}

	private static void sendSingle(long serverId, YanQuMessage packet) {

		for (ServerConnectionGroup group : agentServerLinkedSet.get(serverId).values()) {
			group.write(packet.getPlayerId(), packet);
			break;
		}
	}

	public static void send(long serverId, YanQuMessage packet) {
		send(serverId, packet.getPlayerId(), packet);
	}

	public static void sendAll(YanQuMessage packet) {
		for (Map<Integer, ServerConnectionGroup> agentLinkedSet : agentServerLinkedSet.values()) {
			for (ServerConnectionGroup group : agentLinkedSet.values()) {
				group.write(packet.getPlayerId(), packet);
			}
		}
	}

	public static ServerConnectionGroup addAgentLinkedGroup(long serverId, int serverIndex, YanQuConnection connection, int count, int linkIndex) {
		synchronized (lockObj) {
			if (!agentServerLinkedSet.containsKey(serverId)) {
				agentServerLinkedSet.put(serverId, new HashMap<>());
			}

			if (agentServerLinkedSet.get(serverId).containsKey(serverIndex)) {
				agentServerLinkedSet.get(serverId).get(serverIndex).addConnection(connection, linkIndex);
			} else {
				ServerConnectionGroup group = new ServerConnectionGroup(count);
				group.setServerId(serverId);
				group.addConnection(connection, linkIndex);
				agentServerLinkedSet.get(serverId).put(serverIndex, group);
			}
			return agentServerLinkedSet.get(serverId).get(serverIndex);
		}
	}

	public static void syncCheckMsgToGate(int serverType) {
		LoadProto.CheckCrossConnectMsg.Builder builder = LoadProto.CheckCrossConnectMsg.newBuilder();
		builder.setServerType(serverType);
		builder.setId(ConfigHelper.getInt("serverId"));
		for (Map<Integer, ServerConnectionGroup> map : agentServerLinkedSet.values()) {
			if (map.containsKey(0)) {
				YanQuMessage message = YanQuMessageUtils.buildMessage(AgentProtocol.A_CROSS_HEART_CHECK, builder);
				map.get(0).connectionSendMsg(message);
			}
		}
		checkIndex ++;
	}


	public static void closeConnection(ChannelHandlerContext ctx) {
		ServerConnectionGroup client = getLinkedClient(ctx);
		if (client == null) {
			return;
		}

		if (!agentServerLinkedSet.containsKey(client.getServerId())) {
			return;
		}
		int id = Long.valueOf(client.getConnectionId()).intValue();
		logger.error("removeLinkedClient --- removeId : {}", id);
		if (agentServerLinkedSet.get(client.getServerId()).containsKey(id)) {
			Connection[] connections = agentServerLinkedSet.get(client.getServerId()).get(id).getAllConnections();
			for (Connection connection : connections) {
				logger.error("s remoteAddress: {}, d remoteAddress: {}", ctx.channel().remoteAddress(), ((YanQuConnection)connection).getChannel().remoteAddress());
				if (ctx.channel().remoteAddress().equals(((YanQuConnection)connection).getChannel().remoteAddress())) {
					connection.close();
					logger.error("remove connection Address : {}", ctx.channel().remoteAddress());
					break;
				}
			}
			boolean isAllDown = true;
			for (Connection connection : connections) {
				if (connection.isAlive()) {
					isAllDown = false;
					break;
				}
			}
			if (isAllDown) {
				synchronized (lockObj) {
					agentServerLinkedSet.get(client.getServerId()).remove(id);
				}
				logger.error("remove client serverId : {}", client.getServerId());
			}
		}
	}

	public static boolean isSeverIdConnection(long serverId){
		if (!agentServerLinkedSet.containsKey(serverId) || !agentCountMap.containsKey(serverId)) {
			return false;
		}
		return true;
	}


	public static long randomConnectionSeverId() {
		if (agentServerLinkedSet.size() == 0) {
			return 0;
		}
		List<Long> list = new ArrayList<>(agentServerLinkedSet.keySet());
		return list.get(new Random().nextInt(list.size()));
	}

}
